<?php


namespace App\Service\Client;

use App\Amqp\DelayProducer;
use App\Amqp\Producer\NoticeTaskArbitrationProducer;
use App\Amqp\Producer\TaskAuditWaitProducer;
use App\Amqp\Producer\TaskAutoPaymentProducer;
use App\Amqp\Producer\TaskNotAuditNoticeProducer;
use App\Amqp\Producer\TaskReturnProducer;
use App\Constants\DefaultValue;
use App\Constants\ErrorCode;
use App\Constants\RedisKey;
use App\Dao\CooperativeUserDao;
use App\Dao\NoticeCooperativeDao;
use App\Dao\TaskDao;
use App\Dao\TaskExecDao;
use App\Dao\TaskTagDao;
use App\Dao\UserDao;
use App\Exception\BusinessException;
use App\Model\Task;
use App\Model\TaskArbitration;
use App\Model\TaskExec;
use App\Model\TaskStep;
use App\Service\Common\TaskLogic;
use App\Utils\LevelRule;
use App\Utils\Lock;
use App\Utils\Log;
use App\Utils\Notice\CooperativeTaskArbitration;
use App\Utils\Notice\EmployerTaskArbitration;
use App\Utils\Notice\Sender;
use App\Utils\Notice\UserTaskNotAuditNotice;
use Hyperf\Amqp\Result;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use App\Constants\Model\Task as TaskConstants;
use App\Constants\Model\TaskExec as TaskExecConstants;
use App\Constants\Model\TaskArbitration as TaskArbitrationConstants;
use App\Constants\Model\Notice as NoticeConstants;
use Hyperf\Validation\ValidationRuleParser;


class TaskService extends BaseService
{
    /**
     * @Inject()
     * @var DelayProducer
     */
    protected $delayProducer;

    /**
     * @Inject()
     * @var TaskLogic
     */
    protected $taskLogic;

    /**
     * @Inject()
     * @var UserDao
     */
    protected $userDao;

    /**
     * @Inject()
     * @var TaskDao
     */
    protected $taskDao;

    /**
     * @Inject()
     * @var TaskTagDao
     */
    protected $taskTagDao;

    /**
     * @Inject()
     * @var TaskExecDao
     */
    protected $taskExecDao;


    /**
     * @Inject()
     * @var CooperativeUserDao
     */
    protected $cooperativeUserDao;

    /**
     * @Inject()
     * @var NoticeCooperativeDao
     */
    protected $noticeCooperativeDao;

    /**
     * 随机任务
     * @return array
     */
    public function random(): array
    {
        $taskTag = $this->taskTagDao->select(['cooperative_id' => $this->userInfo()->cooperative_id]);
        if (count($taskTag) == 0) {
            return [
                'task' => [],
                'taskTag' => ''
            ];
        }

        $taskTag = $taskTag->toArray();

        $taskTagName = count($taskTag) > 0 ? array_column($taskTag, 'tag') : [];
        $randomNameKey = array_rand($taskTagName);

        $where = [
            'cooperative_id' => $this->userInfo()->cooperative_id,
            'show_status' => TaskConstants::SHOW_STATUS_ONLINE,
            'audit_status' => TaskConstants::AUDIT_STATUS_ALLOW,
            'disable_status' => TaskConstants::DISABLE_STATUS_NO,
            'tags' => ['like', "%{$taskTagName[$randomNameKey]}%"]
        ];

        /** @var \Hyperf\Contract\LengthAwarePaginatorInterface $task */
        $task = $this->taskDao->paginate($where, 30, ['*'], ['orderBy' => ['created_at', 'desc']]);

        $taskRandom = [];
        if ($task) {
            $taskArray = $task->items();
            if (count($taskArray) <= 3) {
                $taskRandom = $taskArray;
            } else {
                $keys = array_rand($taskArray, 3);
                array_push($taskRandom, $taskArray[$keys[0]]);
                array_push($taskRandom, $taskArray[$keys[1]]);
                array_push($taskRandom, $taskArray[$keys[2]]);
            }
        }

        /** @var Task $v */
        foreach ($taskRandom as $k => $v) {
            $v['gold'] = $this->price2gold($v->price);
            $taskRandom[$k] = $v;
        }

        return [
            'task' => $taskRandom,
            'taskTag' => $taskTagName[$randomNameKey]
        ];
    }


    /**
     * 任务列表
     * @param array $data
     * @return array
     */
    public function list(array $data): array
    {
        $perPage = isset($data['perPage']) ? (int)$data['perPage'] : DefaultValue::PER_PAGE_DEFAULT;

        $where = [
            ['cooperative_id', $this->userInfo()->cooperative_id],
            ['show_status', TaskConstants::SHOW_STATUS_ONLINE],
            ['audit_status', TaskConstants::AUDIT_STATUS_ALLOW],
            ['disable_status', TaskConstants::DISABLE_STATUS_NO]
        ];

        $query = Task::query()->where($where);

        //模糊查询
        if (isset($data['search']) && $data['search'] != '') {
            $query->where(
                function ($qy) use ($data) {
                    $qy->where('tags', 'like', "%{$data['search']}%")
                        ->orWhere('title', 'like', "%{$data['search']}%");
                }
            );
        }

        if (isset($data['sort']) && $data['sort'] != '') {
            //指定排序
            $sortArray = explode(',', $data['sort']);
            if (count($sortArray) == 2) {
                $query->orderBy($sortArray[1], $sortArray[0]);
            }
        } else {
            //默认排序
            $query->orderBy('created_at', 'desc')
                ->orderBy('price', 'desc');
        }

        $page = $query->paginate($perPage);

        $result = [];
        /** @var Task $v */
        foreach ($page->items() as $v) {
            $v['gold'] = $this->price2gold($v->price);

            $endTimeDay = 0;
            $diffTime = $v->end_time - time();
            if ($diffTime > 0) {
                $endTimeDay = ceil($diffTime / 86400);
            }

            //剩余天数
            $v['end_time_day'] = $endTimeDay;
            $result[] = $v;
        }
        return [
            'currentPage' => $page->currentPage(),//当前页
            'total' => $page->total(),//数据总数
            'hasMorePages' => $page->hasMorePages(),//是否还有更多页
            'data' => $result,//数据
        ];
    }


    /**
     * 任务详情
     * @param $id
     * @return array
     */
    public function info($id): array
    {
        /** @var Task $task */
        $task = $this->taskDao->first(
            ['id' => $id, 'cooperative_id' => $this->userInfo()->cooperative_id]
        );
        if (!$task) {
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }

        $taskSteps = [];

        $taskStepsObj = $task->taskSteps()->orderBy('step_type', 'asc')->get();

        /** @var TaskStep $v */
        foreach ($taskStepsObj as $v) {
            if ($v->step_type == 10) {
                $taskSteps['step_type_1'] = [
                    'step_type' => $v->step_type,
                    'text' => $v->text,
                    'images' => $v->image
                ];
            }

            if ($v->step_type >= 20 && $v->step_type <= 29) {
                $taskSteps['step_type_2'][] = [
                    'step_type' => $v->step_type,
                    'text' => $v->text,
                    'image' => $v->image
                ];
            }

            if ($v->step_type >= 30 && $v->step_type <= 39) {
                $taskSteps['step_type_3'][] = [
                    'step_type' => $v->step_type,
                    'text' => $v->text,
                    'image' => $v->image
                ];
            }
        }

        $endTimeDay = 0;
        $diffTime = $task->end_time - time();
        if ($diffTime > 0) {
            $endTimeDay = ceil($diffTime / 86400);
        }

        $data = [
            'employer_id' => $task->user->id,
            'employer_avatar' => $task->user->avatar != '' ? $task->user->avatar : '',
            'employer_nickname' => $task->user->nickname,
            'employer_personal_certification' => $task->user->personal_certification,
            'employer_company_certification' => $task->user->company_certification,
            'employer_credit_point' => $task->user->employer->credit_point,
            'employer_shop_point' => $task->user->employer->shop_point,
            'task_cover_image' => $task->cover_image != '' ? $task->cover_image : '',
            'task_title' => $task->title,
            'task_product_name' => $task->product_name,
            'task_description' => $task->description,
            'tags' => $task->tags,
            'task_receiver_count' => bcsub((string)$task->count, (string)$task->remaining_count),
            'task_remaining_count' => $task->remaining_count,
            'user_finish_time' => $task->user_finish_time,
            'audit_time' => $task->audit_time,
            'estimated_time' => $task->estimated_time,
            'price' => $task->price,
            'gold' => bcmul((string)$task->price, (string)100),
            'getter_user_info' => $task->getter_user_info,
            'hint_message' => $task->hint_message,
            'task_end_time' => $task->end_time,
            'task_end_time_day' => $endTimeDay,
            'task_count' => $task->count,
            'task_done_count' => $task->done_count,
            'task_finish_status' => $task->finish_status,
            'task_show_status' => $task->show_status,
            'task_audit_status' => $task->audit_status,
            'task_disable_status' => $task->disable_status,
            'audit_fail_message' => $task->message,
            'employer_contact' => $task->contact,
            'is_auto_payment' => $task->auto_payment,
            'created_time' => $task->created_at,
            'step' => $taskSteps
        ];

        return [
            'task' => $data
        ];
    }


    /**
     * 指定任务的用户信息
     * @param $id
     * @param array $data
     * @return array
     */
    public function receiver($id, array $data)
    {
        /** @var Task $task */
        $task = $this->taskDao->first(
            ['id' => $id, 'cooperative_id' => $this->userInfo()->cooperative_id]
        );
        if (!$task) {
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }

        $perPage = isset($data['perPage']) ? (int)$data['perPage'] : DefaultValue::PER_PAGE_DEFAULT;

        $page = $this->taskExecDao
            ->where(['task_id' => $task->id])
            ->with('user')
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);

        $result = [];
        /** @var TaskExec $v */
        foreach ($page->items() as $v) {
            $created_time = '';
            if ($v->created_at) {
                $created_time = strtotime($v->created_at);
                $created_time = date('Y/m/d H:m:i', $created_time);
            }
            $v->created_time = $created_time;
            $v->remaining_time_surplus = ($v->remaining_time - time()) > 0 ? ($v->remaining_time - time()) : 0;

            $result[] = $v;
        }

        return [
            'currentPage' => $page->currentPage(),//当前页
            'total' => $page->total(),//数据总数
            'hasMorePages' => $page->hasMorePages(),//是否还有更多页
            'data' => $result,//数据
        ];
    }


    /**
     * 领取任务
     * @param array $data
     * @return array|string[]
     */
    public function receiveTask(array $data): array
    {
        $where = [
            'id' => $data['id'],
            'cooperative_id' => $this->userInfo()->cooperative_id,
            'show_status' => TaskConstants::SHOW_STATUS_ONLINE,
            'audit_status' => TaskConstants::AUDIT_STATUS_ALLOW,
            'disable_status' => TaskConstants::DISABLE_STATUS_NO
        ];
        /** @var Task $task */
        $task = $this->taskDao->first($where);
        if (!$task) {
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }

        //不能领取自己的任务
        if ($task->employer_id == $this->userInfo()->id) {
            throw new BusinessException(ErrorCode::TASK_IS_SELF);
        }

        if ((float)$task->price > (float)LevelRule::$rule[$this->userInfo()->level]['priceLimit']) {
            $canDoLevel = 1;
            foreach (LevelRule::$rule as $k => $v) {
                $canDoLevel = $k;
                if ((float)$task->price < (float)$v['priceLimit']) {
                    break;
                }
            }

            $errorMessage = "当前任务需要达到{$canDoLevel}级才能领取";
            //eg：因为是可变参数，自定义message
            throw new BusinessException(ErrorCode::LEVEL_NOT_ENOUGH, $errorMessage);
        }

        $taskExecHas = $this->taskExecDao->first(
            [
                'user_id' => $this->userInfo()->id,
                'task_id' => $data['id']
            ]
        );
        if ($taskExecHas) {
            //已经领取该任务
            throw new BusinessException(ErrorCode::TASK_RECEIVE_HAS);
        }

        $lockKey = RedisKey::LOCK_MYSQL_TABLE_ID . 'task:' . $task->id;
        $Lock = new Lock();
        $lockState = $Lock->lock($lockKey, DefaultValue::LOCK_TTL);
        if (!$lockState) {
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }

        /** @var Task $taskRefresh */
        $taskRefresh = $this->taskDao->first($where);

        if ($taskRefresh->remaining_count <= 0) {
            $Lock->unlock($lockKey);
            throw new BusinessException(ErrorCode::TASK_NOT_REMAIN);
        }

        $task_exec_id = '';
        Db::beginTransaction();
        try {
            $taskRefresh->remaining_count = --$taskRefresh->remaining_count;
            $taskRefresh->save();

            $taskExec = new TaskExec();
            $taskExec->cooperative_id = $this->userInfo()->cooperative_id;
            $taskExec->user_id = $this->userInfo()->id;
            $taskExec->employer_id = $task->employer_id;
            $taskExec->task_id = $task->id;
            $taskExec->getter_time = time();
            $taskExec->remaining_time = time() + $task->user_finish_time * 3600;
            $taskExec->status = TaskExecConstants::STATUS_NOT_AUDIT;
            $taskExec->finish_status = TaskExecConstants::FINISH_STATUS_NO;
            $taskExec->save();

            $task_exec_id = $taskExec->id;

            //延迟队列-超时未提交返还任务
            $messageTaskReturn = new TaskReturnProducer(['taskExecId' => $taskExec->id]);
            $this->delayProducer->produce($messageTaskReturn, $task->user_finish_time * 3600);

            //子任务未提交倒计时20分钟-消息通知
            $noticeTime = $task->user_finish_time * 3600 - TaskExecConstants::NOT_AUDIT_NOTICE_TIME;
            if ($noticeTime > 0) {
                $messageTaskNotAuditNotice = new TaskNotAuditNoticeProducer(['taskExecId' => $taskExec->id]);
                $this->delayProducer->produce($messageTaskNotAuditNotice, $noticeTime);
            }

            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            throw new BusinessException(ErrorCode::TASK_RECEIVE_ERROR);
        } finally {
            $Lock->unlock($lockKey);
        }

        return [
            'task_exec_id' => $task_exec_id
        ];
    }


    /**
     * 任务列表-已经领取
     * @param array $data
     * @return array
     */
    public function receiveTaskList(array $data): array
    {
        $perPage = isset($data['perPage']) ? (int)$data['perPage'] : DefaultValue::PER_PAGE_DEFAULT;

        $where = [
            'user_id' => $this->userInfo()->id
        ];
        /**
         * 对应前端实际业务逻辑
         * 用户端任务进度列表状态
         *  1未提交，未结束（正在进行，倒计时）
         *  2未提交，已结束
         *  3已提交，待审核
         *  4已提交，通过
         *  5已提交，未通过
         *  6仲裁，待审
         *  7仲裁，已审核
         */
        switch ((int)$data['type']) {
            case TaskExecConstants::USER_TASK_LIST_STATUS_NOT_AUDIT_ON:
                $where['status'] = TaskExecConstants::STATUS_NOT_AUDIT;
                $where['finish_status'] = TaskExecConstants::FINISH_STATUS_NO;
                break;
            case TaskExecConstants::USER_TASK_LIST_STATUS_NOT_AUDIT_END:
                $where['status'] = TaskExecConstants::STATUS_NOT_AUDIT;
                $where['finish_status'] = TaskExecConstants::FINISH_STATUS_YES;
                break;
            case TaskExecConstants::USER_TASK_LIST_STATUS_AUDIT_NOT:
                $where['status'] = TaskExecConstants::STATUS_HAS_AUDIT;
                $where['audit_status'] = TaskExecConstants::AUDIT_STATUS_NOT;
                break;
            case TaskExecConstants::USER_TASK_LIST_STATUS_AUDIT_SUCCESS:
                $where['status'] = TaskExecConstants::STATUS_HAS_AUDIT;
                $where['audit_status'] = TaskExecConstants::AUDIT_STATUS_SUCCESS;
                break;
            case TaskExecConstants::USER_TASK_LIST_STATUS_AUDIT_FAIL:
                $where['status'] = TaskExecConstants::STATUS_HAS_AUDIT;
                $where['audit_status'] = TaskExecConstants::AUDIT_STATUS_FAIL;
                break;
            case TaskExecConstants::USER_TASK_LIST_STATUS_ARBITRATION_NOT:
                $where['status'] = TaskExecConstants::STATUS_ARBITRATION;
                $where['arbitration_status'] = TaskExecConstants::ARBITRATION_STATUS_NOT;
                break;
            case TaskExecConstants::USER_TASK_LIST_STATUS_ARBITRATION_HAS:
                $where['status'] = TaskExecConstants::STATUS_ARBITRATION;
                $where['arbitration_status'] = TaskExecConstants::ARBITRATION_STATUS_HAS;
        }

        $page = $this->taskExecDao
            ->where($where)
            ->with('task')
            ->with('taskArbitration')
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);

        $data = [];
        /** @var TaskExec $v */
        foreach ($page->items() as $v) {
            $data[] = [
                'task_id' => $v->task->id,
                'task_exec_id' => $v->id,
                'task_cover_image' => $v->task->cover_image,
                'task_title' => $v->task->title,
                'remaining_time' => $v->remaining_time,
                'remaining_time_surplus' => ($v->remaining_time - time()) > 0 ? ($v->remaining_time - time()) : 0,
                'price' => $v->task->price,
                'gold' => $this->price2gold($v->task->price),
                'audit_status' => $v->audit_status,
                'finish_status' => $v->finish_status,
                'status' => $v->status,
                'audit_time_end' => $v->audit_time_end,
                'audit_time_end_surplus' => ($v->audit_time_end - time()) > 0 ? ($v->audit_time_end - time()) : 0,
                'audit_fail_context' => $v->audit_fail_context,
                'audit_fail_overtime' => $v->audit_fail_time != 0 ? bcadd(
                    (string)$v->audit_fail_time,
                    (string)TaskExecConstants::AUDIT_FAIL_OVERTIME
                ) : 0,
                'audit_fail_surplus' => ($v->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time(
                    )) > 0 ? ($v->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time()) : 0,
                'arbitration_status' => $v->arbitration_status,
                'arbitration' => [
                    'status' => isset($v->taskArbitration->status) ? $v->taskArbitration->status : '',
                    'context' => isset($v->taskArbitration->context) ? $v->taskArbitration->context : ''
                ]
            ];
        }

        return [
            'currentPage' => $page->currentPage(),//当前页
            'total' => $page->total(),//数据总数
            'hasMorePages' => $page->hasMorePages(),//是否还有更多页
            'data' => $data,//数据
        ];
    }


    /**
     * 提交/修改任务
     * @param $id
     * @param array $data
     * @throws \Throwable
     */
    public function receiveTaskSubmit($id, array $data): void
    {
        /** @var TaskExec $taskExec */
        $taskExec = $this->taskExecDao
            ->where(
                [
                    'id' => $id,
                    'user_id' => $this->userInfo()->id
                ]
            )
            ->with('task')
            ->first();
        if (!$taskExec) {
            throw new BusinessException(ErrorCode::TASK_RECEIVE_NOT_HAS);
        }

        //是否需要收集用户信息
        if ($taskExec->task->getter_user_info == TaskConstants::GETTER_USER_INFO_YES
            && $data['gather_user_info'] == '') {
            throw new BusinessException(ErrorCode::TASK_GATHER_USER_INFO);
        }


        //任务异常-任务不存在
        if (!$taskExec->task->id) {
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }
        //全部完成
        if ($taskExec->task->finish_status == TaskConstants::FINISH_STATUS_ALL) {
            throw new BusinessException(ErrorCode::TASK_END);
        }

        //已提交通过、仲裁 禁止二次提交
        if ($taskExec->status == TaskExecConstants::STATUS_ARBITRATION
            || $taskExec->audit_status == TaskExecConstants::AUDIT_STATUS_SUCCESS) {
            throw new BusinessException(ErrorCode::TASK_SUBMIT_REPEAT);
        }

        //最大提交失败次数5
        if ($taskExec->audit_fail_number > TaskExecConstants::AUDIT_FAIL_NUMBER_MAX) {
            throw new BusinessException(ErrorCode::TASK_SUBMIT_FAIL_MAX);
        }

        $filePathImageOld = $taskExec->audit_content != '' ? explode('|', $taskExec->audit_content) : [];


        $filePathImage = [];
        for ($i = 0; $i < 3; $i++) {
            $key = "audit_image_{$i}";
            $filePathImage[$i] = $data[$key];
        }

        try {
            if ($taskExec->task->getter_user_info == 1 && $data['gather_user_info'] == '') {
                throw new BusinessException(ErrorCode::TASK_GATHER_USER_INFO);
            }

            $taskExec->audit_status = TaskExecConstants::AUDIT_STATUS_NOT;
            $taskExec->status = TaskExecConstants::AUDIT_STATUS_SUCCESS;
            $taskExec->audit_content = $filePathImage;
            $taskExec->audit_time_end = time() + $taskExec->task->audit_time * 3600;
            if (isset($data['gather_user_info']) && $data['gather_user_info'] != '') {
                $taskExec->gather_user_info = $data['gather_user_info'];
            }
            $taskExec->save();

            //延迟队列-自动打款
            if ($taskExec->task->auto_payment == TaskConstants::AUTO_PAYMENT_YES
                && $taskExec->task->audit_time != 0
            ) {
                $messageTaskAutoPayment = new TaskAutoPaymentProducer(['taskExecId' => $taskExec->id]);
                $this->delayProducer->produce($messageTaskAutoPayment, $taskExec->task->audit_time * 3600);
            }

            //审核倒计时快到期限提醒-20分钟
            $noticeTime = $taskExec->task->audit_time * 3600 - TaskExecConstants::AUDIT_WAIT_TIME;
            if ($noticeTime > 0) {
                $messageTaskAuditWait = new TaskAuditWaitProducer(['taskExecId' => $taskExec]);
                $this->delayProducer->produce($messageTaskAuditWait, $noticeTime);
            }
        } catch (\Throwable $e) {
            Log::get()->error($e->getCode(), [$e->getMessage(), $data]);
            Log::get()->error($e->getTraceAsString());
            throw new BusinessException(ErrorCode::RECEIVER_TASK_SUBMIT_FAIL);
        }
    }


    /**
     * 申请仲裁
     * @param $id
     */
    public function receiveTaskArbitrate($id): void
    {
        /** @var TaskExec $taskExec */
        $taskExec = $this->taskExecDao
            ->where(
                [
                    'id' => $id,
                    'user_id' => $this->userInfo()->id
                ]
            )
            ->with('task')
            ->first();
        if (!$taskExec) {
            throw new BusinessException(ErrorCode::TASK_RECEIVE_NOT_HAS);
        }

        //任务异常-任务不存在
        if (!$taskExec->task->id) {
            throw new BusinessException(ErrorCode::TASK_NOT_EXIST);
        }

        //全部完成
        if ($taskExec->task->finish_status == TaskConstants::FINISH_STATUS_ALL) {
            throw new BusinessException(ErrorCode::TASK_END);
        }

        //禁止二次审核仲裁
        if ($taskExec->status == TaskExecConstants::STATUS_ARBITRATION) {
            throw new BusinessException(ErrorCode::TASK_SUBMIT_REPEAT);
        }

        Db::beginTransaction();
        try {
            $taskExec->status = TaskExecConstants::STATUS_ARBITRATION;
            $taskExec->arbitration_status = TaskExecConstants::ARBITRATION_STATUS_NOT;
            $taskExec->save();

            $taskArbitration = new TaskArbitration();
            $taskArbitration->status = TaskArbitrationConstants::STATUS_NOT;
            $taskArbitration->task_id = $taskExec->task->id;
            $taskArbitration->cooperative_id = $this->userInfo()->cooperative_id;
            $taskArbitration->user_id = $taskExec->user_id;
            $taskArbitration->task_exec_id = $taskExec->id;
            $taskArbitration->context = '';
            $taskArbitration->save();

            //悬赏主端-仲裁通知
            Sender::send(
                [$taskExec->employer_id],
                new EmployerTaskArbitration(
                    [
                        'routeParams' => [
                            'id' => $taskExec->id,
                            'task_id' => $taskExec->task->id
                        ]
                    ]
                )
            );

            //合作商端-仲裁通知
            $message = new NoticeTaskArbitrationProducer(
                [
                    'cooperative_id' => $taskExec->cooperative_id,
                ]
            );
            $this->delayProducer->produce($message, 1);

            Db::commit();
        } catch (\Throwable $e) {
            Db::rollBack();
            Log::get()->error($e->getCode(), [$e->getMessage()]);
            Log::get()->error($e->getTraceAsString());

//            vdump($e->getMessage());
            throw new BusinessException(ErrorCode::APPLY_ARBITRATE_ERROR);
        }
    }


    /**
     * 任务标签
     * @return array
     */
    public function tags(): array
    {
        $tags = $this->taskTagDao->select(
            ['cooperative_id' => $this->userInfo()->cooperative_id]
        );
        if (!$tags) {
            $tags = [];
        }

        return [
            'tags' => $tags
        ];
    }

    /**
     * 任务进度-详情
     * @param $id
     * @return array
     */
    public function processInfo($id): array
    {
        /** @var TaskExec $info */
        $info = $this->taskExecDao
            ->where(['id' => $id])
            ->with('user')
            ->with('taskArbitration')
            ->first();
        if (!$info) {
            throw new BusinessException(ErrorCode::TASK_PROCESS_NOT_EXIST);
        }

        $info->remaining_time_surplus = ($info->remaining_time - time()) > 0 ? ($info->remaining_time - time()) : 0;
        $info->audit_time_end_surplus = ($info->audit_time_end - time()) > 0 ? ($info->audit_time_end - time()) : 0;
        $info->audit_fail_surplus = ($info->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time(
            )) > 0 ? ($info->audit_fail_time + TaskExecConstants::AUDIT_FAIL_OVERTIME - time()) : 0;

        return [
            'info' => $info
        ];
    }

    public function taskReceiverInfo($id)
    {
        /** @var TaskExec $info */
        $info = $this->taskExecDao
            ->where(['user_id' => $this->userInfo()->id, 'task_id' => $id])
            ->first();

        if ($info) {
            return $this->processInfo($info->id);
        } else {
            throw new BusinessException(ErrorCode::TASK_PROCESS_NOT_EXIST);
        }
    }


    /**
     * 任务归还
     * @param array $data
     * @return string
     */
    public function taskReturn(array $data): string
    {
        $Lock = new Lock();

        $lockKeys = [];
        $lockKeyTmp1 = $Lock->locks(
            [
                RedisKey::LOCK_MYSQL_TABLE_ID . 'task_exec:' . $data['taskExecId']
            ],
            DefaultValue::LOCK_TTL
        );
        if (!$lockKeyTmp1) {
            return Result::REQUEUE;
        }
        $lockKeys = array_merge($lockKeys, $lockKeyTmp1);

        /** @var TaskExec $taskExec */
        $taskExec = $this->taskExecDao->first(['id' => $data['taskExecId']]);
        if (!$taskExec) {
            $Lock->unlocks($lockKeys);
            return Result::DROP;
        }

        $hasUnlock = false;
        //任务超时未提交|提交失败48小时未再次提交
        if ($taskExec->status == TaskExecConstants::STATUS_NOT_AUDIT
            || ($taskExec->status == TaskExecConstants::STATUS_HAS_AUDIT
                && $taskExec->audit_status == TaskExecConstants::AUDIT_STATUS_FAIL
                && (time() - TaskExecConstants::AUDIT_FAIL_OVERTIME) >= $taskExec->audit_fail_time)
        ) {
            Db::beginTransaction();
            try {
                //当条任务进度已结束
                $taskExec->finish_status = TaskExecConstants::FINISH_STATUS_YES;
                $taskExec->save();

                //返还任务（如果任务已退款，给悬赏主返款且记录日志）
                $this->taskLogic->taskReturn(
                    [
                        'employer_id' => $taskExec->employer_id,
                        'task_id' => $taskExec->task_id
                    ]
                );

                Db::commit();
            } catch (\Throwable $e) {
                Db::rollBack();
                return Result::REQUEUE;
            } finally {
                $Lock->unlocks($lockKeys);
                $hasUnlock = true;
            }
        }

        if (!$hasUnlock) {
            $Lock->unlocks($lockKeys);
        }

        return Result::ACK;
    }

    /**
     * 子任务距离结束还有20分钟消息通知
     * @param array $data
     * @return string
     */
    public function taskNotAuditNotice(array $data): string
    {
        /** @var TaskExec $taskExec */
        $taskExec = $this->taskExecDao
            ->where(['id' => $data['taskExecId']])
            ->with('task')
            ->first();

        if (!$taskExec) {
            return Result::DROP;
        }

        if ($taskExec->status == TaskExecConstants::STATUS_NOT_AUDIT) {
            Sender::send(
                [$taskExec->user_id],
                new UserTaskNotAuditNotice(
                    [
                        'replaces' => [
                            'task_title' => $taskExec->task->title
                        ],
                        'extension' => [
                            'task_id' => $taskExec->task->id
                        ]
                    ]
                )
            );
        }

        return Result::ACK;
    }

    public function noticeTaskArbitration(array $data)
    {
        /**
         * 获取所有用户
         * 判断两个小时之内是否有发过且未读
         * 无-发送，有忽略
         */
        $cooperativeIds = $this->cooperativeUserDao->getIds($data['cooperative_id']);

        //判断两个小时内是否有发过且未读
        $beforeTime = time() - NoticeConstants::BEFORE_TIME_HAS_NOT_READ;
        $beforeDate = date('Y-m-d H:i:s', $beforeTime);

        $hasNotice = $this->noticeCooperativeDao
            ->where(
                [
                    'type' => NoticeConstants::COOPERATIVE_TASK_ARBITRATION,
                    'status' => NoticeConstants::STATUS_UNREAD,
                    'created_at_gt' => $beforeDate,
                    'cooperative_user_ids' => $cooperativeIds
                ]
            )
            ->get();

        $hasNoticeIds = [];
        if (count($hasNotice) > 0) {
            $hasNoticeArray = $hasNotice->toArray();
            $hasNoticeIds = array_column($hasNoticeArray, 'cooperative_user_id');
        }

        $needSendIds = array_diff($cooperativeIds, $hasNoticeIds);

        if (count($needSendIds) > 0) {
            Sender::send(
                $needSendIds,
                new CooperativeTaskArbitration()
            );
        }

        return Result::ACK;
    }
}